﻿using System;
using static CyberU;
using static CyberU.UI;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering.UI;
using UnityEngine.Serialization;

public class Graph : BaseUI
{
    public bool isAddingLink;
    public ClickText saveButton;
    public ClickText reloadButton;

    public string jsonPath = "K:\\autom\\repositories\\Unity\\CyberU\\Assets\\Resources\\Scripts\\CyberU\\Graph\\sample.json";
    public GraphData data;

    public int n
    {
        get { return data.nodeDatas.Count; }
    }

    public GraphNode _selectedNode;

    public GraphNode selectedNode
    {
        get { return _selectedNode; }
        set
        {
            if (_selectedNode != null)
            {
                _selectedNode.selected = false;
            }

            _selectedNode = value;
            if (value != null)
            {
                value.selected = true;
            }
        }
    }

    public void SelectNode(GraphNode node)
    {
        selectedNode = node;
    }

    public int edgeNum
    {
        get { return data.edges.Count; }
    }

    public GraphNode AddNode(GraphNodeData nodeData = null, GraphNode parentNode = null, bool addData = true)
    {
        if (nodeData == null)
        {
            nodeData = new GraphNodeData();
            nodeData.id = $"{n + 1}";
        }

        // Delog($"{nodeData.id}");
        GameObject _ = FromPrefab($"GraphNode", gameObject, name: $"Node_{nodeData.id}", position: parentNode ? parentNode.transform.position : new Vector3());
        GraphNode node = AddOrGetComponent<GraphNode>(_);
        // Delogb(node);
        node.graph = this;
        // Delog("added graph");
        nodeData.node = node;
        node.data = nodeData;
        if (addData)
        {
            data.nodeDatas.Add(nodeData);
        }

        return node;
    }

    public void Awake()
    {
        // saveButton.AddClickListener(()=>data.Save("K:\\autom\\repositories\\Unity\\CyberU\\Assets\\Resources\\Scripts\\CyberU\\Graph\\temp.json"));
        saveButton = FindInChild<ClickText>(gameObject, name: "SaveButton");
        reloadButton = FindInChild<ClickText>(gameObject, name: "ReloadButton");
        saveButton.AddClickListener(() => Save());
        reloadButton.AddClickListener(() => Reload());
    }

    public void Save()
    {
        data.Save(jsonPath);
        // Deloga(uiCanvas.transform);
        ChangeParent(saveButton, uiCanvas);
        ChangeParent(reloadButton, uiCanvas);
    }

    public void Start()
    {
        // SetFrame(135);
        Reload();
        AutoSave();
        // Delog(data.nodes[0].id);
        // Delog(data.edges[0].fromId);
    }

    public void Reload()
    {
        ClearChildren(gameObject, exclude: new List<object> { reloadButton, saveButton });
        data = GraphData.Load(jsonPath);
        // Delog(data.nodes[0].id);
        // Delog(data.nodes[0].name);
        // Delog(data.nodes[0].serializableColor);
        // Delog(data.nodes.Count);
        data.jsonPath = jsonPath;
        ReBuild();
    }

    public void ReBuild()
    {
        LoadNodes();
        LoadEdges();
    }

    void LoadNodes()
    {
        foreach (var nodeData in data.nodeDatas)
        {
            // Delog($"{nodeData.id}");
            GraphNode graphNode = this.AddNode(nodeData, addData: false);
        }
    }

    public string EdgeSprite = "Sprites/Default";

    public GraphEdge AddEdge(GraphEdgeData edgeData = null, object from = null, object to = null, bool addData = true)
    {
        if (from == null)
        {
            from = FindNode(edgeData.fromId);
            to = FindNode(edgeData.toId);
            // Delog(edgeData.fromId, from);
        }

        GraphNode fromNode = AsComponent<GraphNode>(from);
        GraphNode toNode = AsComponent<GraphNode>(to);
        GameObject _ = FromPrefab("GraphEdge", gameObject, $"Edge_{fromNode.data.id}_to_{toNode.data.id}");
        LineRenderer line = AddOrGetComponent<LineRenderer>(_);
        line.startWidth = 0.1f;
        line.endWidth = 0.1f;
        line.material = new Material(Shader.Find(EdgeSprite));
        line.startColor = Color.white;
        line.endColor = Color.white;
        GraphEdge graphEdge = AddOrGetComponent<GraphEdge>(_);
        graphEdge.graph = this;
        graphEdge.lineRenderer = line;
        // Delog($"{FindNode(edgeData.fromId).gameObject} 332");
        if (edgeData == null)
        {
            edgeData = new GraphEdgeData(edge: graphEdge);
        }

        graphEdge.Initial(edgeData, AsGameObject(from), AsGameObject(to));
        if (addData)
        {
            graphEdge.graph.data.edgeDatas.Add(edgeData);
        }

        return graphEdge;
    }

    void LoadEdges()
    {
        foreach (var edgeData in data.edgeDatas)
        {
            // Delog(data.edgeDatas[0].fromId);
            GraphEdge graphEdge = AddEdge(edgeData: edgeData, addData: false);
        }
    }

    public GraphNode FindNode(string id)
    {
        // Delog($"{data.nodes.Find(x => x.id == id).node==null}");
        var _ = data.nodes.Find(x => x.id == id);
        return _ == null ? null : _.node;
    }

    public void DeleteEdge(GraphEdgeData edgeData)
    {
        data.edgeDatas.Remove(edgeData);
        DestroyGameObject(edgeData.edge);
    }

    public void AutoSave()
    {
        StartCoroutine((RunCoroutine(() => Save())));
    }
}

[System.Serializable]
public class GraphData
{
    public static GraphData Load(string jsonPath)
    {
        return FromJson<GraphData>(jsonPath);
    }

    public void Save(string path = null)
    {
        // Delog($"saving as {path}");
        foreach (GraphNodeData nodeData in nodes)
        {
            nodeData.UpdateFromNode();
        }

        string savePath = path ?? jsonPath;
        if (string.IsNullOrEmpty(savePath))
        {
            Debug.LogError("No save path specified!");
            return;
        }

        string dir = GetDirectoryName(savePath);
        if (!ExistsDirectory(dir)) CreateDirectory(dir);
        WriteJson(savePath, this);
        // Debug.Log($"Graph saved to: {savePath}");
    }

    [JsonIgnore] public string jsonPath;
    public List<GraphNodeData> nodes = new List<GraphNodeData>();

    [Newtonsoft.Json.JsonIgnore]
    public List<GraphNodeData> nodeDatas
    {
        get { return nodes; }
        set { nodes = value; }
    }

    // public List<GraphEdgeData> edges;
    public List<GraphEdgeData> edges = new List<GraphEdgeData>();

    [Newtonsoft.Json.JsonIgnore]
    public List<GraphEdgeData> edgeDatas
    {
        get { return edges; }
        set { edges = value; }
    }
}